﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Activities;
using Microsoft.TeamFoundation.Build.Workflow.Activities;
using System.Drawing;
using System.ComponentModel;
using Microsoft.TeamFoundation.Build.Workflow.Tracking;
using Microsoft.TeamFoundation.Build.Workflow.Design;
using Microsoft.VisualBasic.Activities;
using System.Activities.Statements;
using System.Globalization;
using Microsoft.TeamFoundation.Build.Client;
using System.Activities.Presentation;
using System.Diagnostics;
using System.Threading;
using System.Runtime.InteropServices;
using System.IO;
using CloudActivities = TfsCloudDeploy.Workflow.Core.Activities;
using System.Windows;

namespace TfsCloudDeploy.Workflow.Core.Activities
{
    /// <summary>
    /// Reflected from: Microsoft.TeamFoundation.Build.Workflow.Activities.InvokeProcess.
    /// Assembly: Microsoft.TeamFoundation.Build.Workflow, Version=10.0.0.0.
    /// Modified to allow hiding the command line arguments so that sensitive information is not displayed in build logs.
    /// This was necessary in order to support database workflow deployments where SQL authentication was used and the 
    /// username and password were embedded in the connection string.
    /// </summary>
    [CloudActivities.SRDescription(typeof(ActivitiesResources), "InvokeProcessDescription"), Designer(typeof(InvokeProcessDesigner)), BuildCategory, ToolboxBitmap(typeof(TeamBuildBaseActivityDesigner), "DefaultBuildIcon.png"), ActivityTracking(ActivityTrackingOption.ActivityOnly)]
    public sealed class InvokeProcessEx : Activity<int>, IActivityTemplateFactory
    {
        // Fields
        private InArgument<Encoding> m_outputEncoding;
        private bool m_outputEncodingSet;

        // Methods
        public InvokeProcessEx()
        {
            Func<Activity> func = null;
            this.m_outputEncoding = new InArgument<Encoding>(new VisualBasicValue<Encoding> { ExpressionText = "System.Text.Encoding.GetEncoding(System.Globalization.CultureInfo.InstalledUICulture.TextInfo.OEMCodePage)" });
            if (func == null)
            {
                func = () => this.CreateBody();
            }
            base.Implementation = func;
        }

        private static string ComposeCommandLine(string fileName, string arguments, bool hideArguments)
        {
            StringBuilder builder = new StringBuilder();
            builder.Append(fileName);
            if (!string.IsNullOrEmpty(arguments) && !hideArguments)
            {
                builder.Append(" ");
                builder.Append(arguments);
            }
            return builder.ToString();
        }

        private Activity CreateBody()
        {
            Variable<string> variable = new Variable<string>();
            Variable<string> variable2 = new Variable<string>();
            return new Sequence { Variables = { variable, variable2 }, Activities = { new Assign<string> { Value = new InArgument<string>(env => Guid.NewGuid().ToString("D", CultureInfo.InvariantCulture)), To = new OutArgument<string>(variable) }, new Assign<string> { Value = new InArgument<string>(env => Guid.NewGuid().ToString("D", CultureInfo.InvariantCulture)), To = new OutArgument<string>(variable2) }, new Parallel { Branches = { new InvokeActionOnInput<string> { BookmarkName = new InArgument<string>(variable), Action = this.OutputDataReceived }, new InvokeActionOnInput<string> { BookmarkName = new InArgument<string>(variable2), Action = this.ErrorDataReceived }, new Sequence { Activities = { new WriteBuildMessage { Importance = new InArgument<BuildMessageImportance>(BuildMessageImportance.High), Message = new InArgument<string>(env => ComposeCommandLine(this.FileName.Get(env), this.Arguments.Get(env), this.HideArguments.Get(env))) }, new InvokeProcessInternal { Output = variable, Error = variable2, FileName = new InArgument<string>(env => this.FileName.Get(env)), Arguments = new InArgument<string>(env => this.Arguments.Get(env)), OutputEncoding = new InArgument<Encoding>(env => this.OutputEncoding.Get(env)), WorkingDirectory = new InArgument<string>(env => this.WorkingDirectory.Get(env)), EnvironmentVariables = new InArgument<IDictionary<string, string>>(env => this.EnvironmentVariables.Get(env)), Result = new OutArgument<int>(env => this.Result.GetLocation(env).Value) } } } } } } };
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeOutputEncoding()
        {
            return this.m_outputEncodingSet;
        }

        Activity IActivityTemplateFactory.Create(DependencyObject target)
        {
            return new InvokeProcessEx { OutputDataReceived = new ActivityAction<string> { Argument = new DelegateInArgument<string> { Name = "stdOutput" } }, ErrorDataReceived = new ActivityAction<string> { Argument = new DelegateInArgument<string> { Name = "errOutput" } } };
        }

        // Properties
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), CloudActivities.SRDescription(typeof(ExtendedWorkflowResources), "HideArgumentsDescription"), DefaultValue((bool)false), Browsable(true), CloudActivities.SRCategory(typeof(ActivitiesResources), "ActivityCategoryMiscellaneous")]
        public InArgument<bool> HideArguments { get; set; }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), CloudActivities.SRDescription(typeof(ActivitiesResources), "InvokeProcessArgumentsDescription"), DefaultValue((string)null), Browsable(true), CloudActivities.SRCategory(typeof(ActivitiesResources), "ActivityCategoryMiscellaneous")]
        public InArgument<string> Arguments { get; set; }

        [CloudActivities.SRCategory(typeof(ActivitiesResources), "ActivityCategoryMiscellaneous"), CloudActivities.SRDescription(typeof(ActivitiesResources), "InvokeProcessEnvironmentVariablesDescription"), Browsable(true), DefaultValue((string)null), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public InArgument<IDictionary<string, string>> EnvironmentVariables { get; set; }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), CloudActivities.SRCategory(typeof(ActivitiesResources), "ActivityCategoryMiscellaneous"), CloudActivities.SRDescription(typeof(ActivitiesResources), "InvokeProcessErrorDataReceivedDescription"), DefaultValue((string)null), Browsable(false)]
        public ActivityAction<string> ErrorDataReceived { get; set; }

        [CloudActivities.SRDescription(typeof(ActivitiesResources), "InvokeProcessFileNameDescription"), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), RequiredArgument, CloudActivities.SRCategory(typeof(ActivitiesResources), "ActivityCategoryMiscellaneous"), Browsable(true), DefaultValue((string)null)]
        public InArgument<string> FileName { get; set; }

        [CloudActivities.SRDescription(typeof(ActivitiesResources), "InvokeProcessOutputDataReceivedDescription"), Browsable(false), DefaultValue((string)null), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), CloudActivities.SRCategory(typeof(ActivitiesResources), "ActivityCategoryMiscellaneous")]
        public ActivityAction<string> OutputDataReceived { get; set; }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), Browsable(true), CloudActivities.SRDescription(typeof(ActivitiesResources), "InvokeProcessOutputEncodingDescription"), CloudActivities.SRCategory(typeof(ActivitiesResources), "ActivityCategoryMiscellaneous")]
        public InArgument<Encoding> OutputEncoding
        {
            get
            {
                return this.m_outputEncoding;
            }
            set
            {
                this.m_outputEncoding = value;
                this.m_outputEncodingSet = true;
            }
        }

        [CloudActivities.SRCategory(typeof(ActivitiesResources), "ActivityCategoryMiscellaneous"), DefaultValue((string)null), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), Browsable(true), CloudActivities.SRDescription(typeof(ActivitiesResources), "InvokeProcessWorkingDirectoryDescription")]
        public InArgument<string> WorkingDirectory { get; set; }

        // Nested Types
        private sealed class InvokeProcessInternal : AsyncCodeActivity<int>
        {
            // Fields
            private static readonly object CancelledKey = new object();
            private static readonly object ProcessKey = new object();

            // Methods
            protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
            {
                Func<AsyncState, int> func = new Func<AsyncState, int>(this.RunCommand);
                AsyncState arg = new AsyncState
                {
                    ActionExtension = context.GetExtension<ScheduleActionExtension>(),
                    ErrorBookmark = this.Error.Get(context),
                    OutputBookmark = this.Output.Get(context),
                    RunCommand = func,
                    StartInfo = this.CreateStartInfo(context)
                };
                context.UserState = arg;
                return func.BeginInvoke(arg, callback, state);
            }

            protected override void CacheMetadata(CodeActivityMetadata metadata)
            {
                base.CacheMetadata(metadata);
                metadata.AddDefaultExtensionProvider<ScheduleActionExtension>(() => new ScheduleActionExtension());
            }

            protected override void Cancel(AsyncCodeActivityContext context)
            {
                AsyncState userState = (AsyncState)context.UserState;
                lock (userState)
                {
                    if (userState.Process != null)
                    {
                        userState.Process.Cancel();
                    }
                    userState.Cancelled = true;
                }
            }

            private ProcessStartInfo CreateStartInfo(AsyncCodeActivityContext context)
            {
                ProcessStartInfo info = new ProcessStartInfo(this.FileName.Get(context));
                string str = this.Arguments.Get(context);
                if (str != null)
                {
                    info.Arguments = str;
                }
                info.CreateNoWindow = true;
                info.ErrorDialog = false;
                info.LoadUserProfile = false;
                info.RedirectStandardError = this.Error.Get(context) != null;
                info.RedirectStandardInput = true;
                info.RedirectStandardOutput = this.Output.Get(context) != null;
                info.UseShellExecute = false;
                Encoding encoding = this.OutputEncoding.Get(context);
                if (encoding != null)
                {
                    if (info.RedirectStandardError)
                    {
                        info.StandardErrorEncoding = encoding;
                    }
                    if (info.RedirectStandardOutput)
                    {
                        info.StandardOutputEncoding = encoding;
                    }
                }
                string str2 = this.WorkingDirectory.Get(context);
                if (str2 != null)
                {
                    info.WorkingDirectory = str2;
                }
                IDictionary<string, string> dictionary = this.EnvironmentVariables.Get(context);
                if (dictionary != null)
                {
                    foreach (KeyValuePair<string, string> pair in dictionary)
                    {
                        info.EnvironmentVariables[pair.Key] = pair.Value;
                    }
                }
                return info;
            }

            protected override int EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
            {
                return ((AsyncState)context.UserState).RunCommand.EndInvoke(result);
            }

            private int RunCommand(AsyncState state)
            {
                int exitCode;
                InvokeProcessEx.ProcessWrapper wrapper = new InvokeProcessEx.ProcessWrapper(state.StartInfo);
                Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs> action = delegate(object sender, InvokeProcessEx.ProcessDataReceivedEventArgs e)
                {
                    string item = e.Data;
                    if (item != null)
                    {
                        item = item.Trim();
                    }
                    state.ActionExtension.ScheduleAction<string>(state.OutputBookmark, item);
                };
                Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs> action2 = delegate(object sender, InvokeProcessEx.ProcessDataReceivedEventArgs e)
                {
                    string data = e.Data;
                    if (data != null)
                    {
                        data = data.Trim();
                    }
                    state.ActionExtension.ScheduleAction<string>(state.ErrorBookmark, data);
                };
                wrapper.OutputDataReceived += action;
                wrapper.ErrorDataReceived += action2;
                try
                {
                    lock (state)
                    {
                        if (state.Cancelled)
                        {
                            return 0;
                        }
                        state.Process = wrapper;
                        wrapper.Start();
                    }
                    wrapper.WaitForExit();
                    exitCode = wrapper.ExitCode;
                }
                finally
                {
                    wrapper.ErrorDataReceived -= action2;
                    wrapper.OutputDataReceived -= action;
                    state.ActionExtension.Shutdown(state.ErrorBookmark);
                    state.ActionExtension.Shutdown(state.OutputBookmark);
                }
                return exitCode;
            }

            // Properties
            public InArgument<string> Arguments { get; set; }

            public InArgument<IDictionary<string, string>> EnvironmentVariables { get; set; }

            public InArgument<string> Error { get; set; }

            public InArgument<string> FileName { get; set; }

            public InArgument<string> Output { get; set; }

            public InArgument<Encoding> OutputEncoding { get; set; }

            public InArgument<string> WorkingDirectory { get; set; }

            // Nested Types
            private sealed class AsyncState
            {
                // Properties
                public ScheduleActionExtension ActionExtension { get; set; }

                public bool Cancelled { get; set; }

                public string ErrorBookmark { get; set; }

                public string OutputBookmark { get; set; }

                public InvokeProcessEx.ProcessWrapper Process { get; set; }

                public Func<InvokeProcessEx.InvokeProcessInternal.AsyncState, int> RunCommand { get; set; }

                public ProcessStartInfo StartInfo { get; set; }
            }
        }

        private class ProcessDataReceivedEventArgs : EventArgs
        {
            // Methods
            public ProcessDataReceivedEventArgs(string data)
            {
                this.Data = data;
            }

            // Properties
            public string Data { get; private set; }
        }

        private class ProcessWrapper
        {
            // Fields
            private Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs> m_errorDataReceived;
            private EventHandler m_exited;
            private int m_asyncStreamReaderCount;
            private ManualResetEvent m_exitedEvent = new ManualResetEvent(false);
            private Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs> m_outputDataReceived;
            private static char[] s_glyphReplacements = new char[] { 
                '☺', '☻', '♥', '♦', '♣', '♠', '•', '◘', '○', '◙', '♂', '♀', '♪', '♫', '♬', '►', 
                '◄', '↕', '‼', '\x00b6', '\x00a7', '▬', '↨', '↑', '↓', '→', '←', '∟', '↔', '▲', '▼', '⌂'
            };

            // Events
            public event Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs> ErrorDataReceived
            {
                add
                {
                    Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs> action2;
                    Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs> errorDataReceived = this.m_errorDataReceived;
                    do
                    {
                        action2 = errorDataReceived;
                        Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs> action3 = (Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs>)Delegate.Combine(action2, value);
                        errorDataReceived = Interlocked.CompareExchange<Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs>>(ref this.m_errorDataReceived, action3, action2);
                    }
                    while (errorDataReceived != action2);
                }
                remove
                {
                    Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs> action2;
                    Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs> errorDataReceived = this.m_errorDataReceived;
                    do
                    {
                        action2 = errorDataReceived;
                        Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs> action3 = (Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs>)Delegate.Remove(action2, value);
                        errorDataReceived = Interlocked.CompareExchange<Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs>>(ref this.m_errorDataReceived, action3, action2);
                    }
                    while (errorDataReceived != action2);
                }
            }

            public event EventHandler Exited
            {
                add
                {
                    EventHandler handler2;
                    EventHandler exited = this.m_exited;
                    do
                    {
                        handler2 = exited;
                        EventHandler handler3 = (EventHandler)Delegate.Combine(handler2, value);
                        exited = Interlocked.CompareExchange<EventHandler>(ref this.m_exited, handler3, handler2);
                    }
                    while (exited != handler2);
                }
                remove
                {
                    EventHandler handler2;
                    EventHandler exited = this.m_exited;
                    do
                    {
                        handler2 = exited;
                        EventHandler handler3 = (EventHandler)Delegate.Remove(handler2, value);
                        exited = Interlocked.CompareExchange<EventHandler>(ref this.m_exited, handler3, handler2);
                    }
                    while (exited != handler2);
                }
            }

            public event Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs> OutputDataReceived
            {
                add
                {
                    Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs> action2;
                    Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs> outputDataReceived = this.m_outputDataReceived;
                    do
                    {
                        action2 = outputDataReceived;
                        Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs> action3 = (Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs>)Delegate.Combine(action2, value);
                        outputDataReceived = Interlocked.CompareExchange<Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs>>(ref this.m_outputDataReceived, action3, action2);
                    }
                    while (outputDataReceived != action2);
                }
                remove
                {
                    Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs> action2;
                    Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs> outputDataReceived = this.m_outputDataReceived;
                    do
                    {
                        action2 = outputDataReceived;
                        Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs> action3 = (Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs>)Delegate.Remove(action2, value);
                        outputDataReceived = Interlocked.CompareExchange<Action<object, InvokeProcessEx.ProcessDataReceivedEventArgs>>(ref this.m_outputDataReceived, action3, action2);
                    }
                    while (outputDataReceived != action2);
                }
            }

            // Methods
            public ProcessWrapper(ProcessStartInfo startInfo)
            {
                this.StartInfo = startInfo;
                this.Lock = new object();
            }

            private void BeginReadStream(StreamReader reader, EventHandler<InvokeProcessEx.ProcessDataReceivedEventArgs> dataHandler)
            {
                this.m_asyncStreamReaderCount++;
                this.ReadStream.BeginInvoke(reader, dataHandler, new AsyncCallback(this.ReadStreamComplete), null);
            }

            public void Cancel()
            {
                lock (this.Lock)
                {
                    if (this.Process != null)
                    {
                        try
                        {
                            if (!this.Process.HasExited)
                            {
                                bool flag = false;
                                if (!this.Job.IsInvalid)
                                {
                                    flag = this.Job.KillProcessTree();
                                }
                                if (!flag)
                                {
                                    this.Process.Kill();
                                }
                            }
                        }
                        catch (InvalidOperationException)
                        {
                        }
                        finally
                        {
                            this.Process = null;
                        }
                    }
                }
            }

            private void ErrorDataHandler(object sender, InvokeProcessEx.ProcessDataReceivedEventArgs e)
            {
                if (this.m_errorDataReceived != null)
                {
                    this.m_errorDataReceived(this, e);
                }
            }

            private void OutputDataHandler(object sender, InvokeProcessEx.ProcessDataReceivedEventArgs e)
            {
                if (this.m_outputDataReceived != null)
                {
                    this.m_outputDataReceived(this, e);
                }
            }

            private void ProcessExitedHandler(object sender, EventArgs e)
            {
                lock (this.Lock)
                {
                    this.m_exitedEvent.Set();
                    if (this.m_exited != null)
                    {
                        this.m_exited(this, e);
                    }
                }
            }

            private void ReadStreamComplete(IAsyncResult result)
            {
                lock (this.Lock)
                {
                    if (Interlocked.Decrement(ref this.m_asyncStreamReaderCount) == 0)
                    {
                        this.ProcessExitedHandler(this.Process, EventArgs.Empty);
                    }
                }
            }

            private void ReadStreamCore(StreamReader reader, EventHandler<InvokeProcessEx.ProcessDataReceivedEventArgs> dataHandler)
            {
                while (!reader.EndOfStream)
                {
                    string input = reader.ReadLine();
                    if (dataHandler != null)
                    {
                        dataHandler(this.Process, new InvokeProcessEx.ProcessDataReceivedEventArgs(ReplaceControlCharacters(input)));
                    }
                }
            }

            private static string ReplaceControlCharacters(string input)
            {
                StringBuilder builder = null;
                for (int i = 0; i < input.Length; i++)
                {
                    if ((input[i] <= '\x001f') || (input[i] == '\x007f'))
                    {
                        if (builder == null)
                        {
                            builder = new StringBuilder(input.Length);
                            builder.Append(input, 0, i);
                        }
                        if (input[i] != '\0')
                        {
                            builder.Append(s_glyphReplacements[(input[i] == '\x007f') ? (s_glyphReplacements.Length - 1) : (input[i] - '\x0001')]);
                        }
                    }
                    else if (builder != null)
                    {
                        builder.Append(input[i]);
                    }
                }
                if (builder != null)
                {
                    return builder.ToString();
                }
                return input;
            }

            public void Start()
            {
                this.ReadStream = new ReadStreamHandler(this.ReadStreamCore);
                this.Process = new Process();
                this.Process.EnableRaisingEvents = true;
                if (!this.StartInfo.RedirectStandardError && !this.StartInfo.RedirectStandardOutput)
                {
                    this.Process.Exited += new EventHandler(this.ProcessExitedHandler);
                }
                this.Process.StartInfo = this.StartInfo;
                lock (this.Lock)
                {
                    try
                    {
                        this.Process.Start();
                    }
                    catch (Exception exception)
                    {
                        if (this.Process != null)
                        {
                            this.Process.Close();
                            this.Process = null;
                        }
                        Win32Exception exception2 = exception as Win32Exception;
                        if ((exception2 != null) && (exception2.NativeErrorCode == 2))
                        {
                            throw new FileNotFoundException(ActivitiesResources.Format("FileNotFoundError", new object[] { this.StartInfo.FileName }), this.StartInfo.FileName, exception);
                        }
                        throw;
                    }
                    this.Process.StandardInput.Close();
                    if (this.StartInfo.RedirectStandardError)
                    {
                        this.BeginReadStream(this.Process.StandardError, new EventHandler<InvokeProcessEx.ProcessDataReceivedEventArgs>(this.ErrorDataHandler));
                    }
                    if (this.StartInfo.RedirectStandardOutput)
                    {
                        this.BeginReadStream(this.Process.StandardOutput, new EventHandler<InvokeProcessEx.ProcessDataReceivedEventArgs>(this.OutputDataHandler));
                    }
                    this.Job = new JobHandle(Guid.NewGuid().ToString());
                    if (!this.Job.IsInvalid && !this.Job.AddProcess(this.Process))
                    {
                        this.Job.Close();
                    }
                }
            }

            public void WaitForExit()
            {
                if (this.Process == null)
                {
                    throw new InvalidOperationException();
                }
                this.m_exitedEvent.WaitOne();
            }

            // Properties
            public int ExitCode
            {
                get
                {
                    if (this.Process == null)
                    {
                        return -1;
                    }
                    return this.Process.ExitCode;
                }
            }

            private JobHandle Job { get; set; }

            private object Lock { get; set; }

            private Process Process { get; set; }

            private ReadStreamHandler ReadStream { get; set; }

            private ProcessStartInfo StartInfo { get; set; }

            // Nested Types
            private class JobHandle : SafeHandle
            {
                // Methods
                public JobHandle(string jobName)
                    : base(IntPtr.Zero, true)
                {
                    NativeMethods.SECURITY_ATTRIBUTES lpJobAttributes = new NativeMethods.SECURITY_ATTRIBUTES
                    {
                        bInheritHandle = true,
                        lpSecurityDescriptor = IntPtr.Zero,
                        nLength = Marshal.SizeOf(typeof(NativeMethods.SECURITY_ATTRIBUTES))
                    };
                    base.SetHandle(NativeMethods.CreateJobObject(ref lpJobAttributes, jobName));
                }

                public bool AddProcess(Process process)
                {
                    using (InvokeProcessEx.ProcessWrapper.ProcessHandle handle = new InvokeProcessEx.ProcessWrapper.ProcessHandle(process.Id))
                    {
                        if (!handle.IsInvalid && NativeMethods.AssignProcessToJobObject(this, handle))
                        {
                            return true;
                        }
                    }
                    return false;
                }

                public bool KillProcessTree()
                {
                    return NativeMethods.TerminateJobObject(this, 1);
                }

                protected override bool ReleaseHandle()
                {
                    return NativeMethods.CloseHandle(base.handle);
                }

                // Properties
                public override bool IsInvalid
                {
                    get
                    {
                        if (!base.IsClosed)
                        {
                            return (base.handle == IntPtr.Zero);
                        }
                        return true;
                    }
                }
            }

            private class ProcessHandle : SafeHandle
            {
                // Fields
                private const uint PROCESS_ALL_ACCESS = 0x1f0fff;

                // Methods
                public ProcessHandle(int processId)
                    : base(NativeMethods.INVALID_HANDLE_VALUE, true)
                {
                    base.SetHandle(NativeMethods.OpenProcess(0x1f0fff, false, (uint)processId));
                }

                protected override bool ReleaseHandle()
                {
                    return NativeMethods.CloseHandle(base.handle);
                }

                // Properties
                public override bool IsInvalid
                {
                    get
                    {
                        if (!base.IsClosed)
                        {
                            return (base.handle == NativeMethods.INVALID_HANDLE_VALUE);
                        }
                        return true;
                    }
                }
            }

            private delegate void ReadStreamHandler(StreamReader reader, EventHandler<InvokeProcessEx.ProcessDataReceivedEventArgs> dataHandler);
        }
    }
}
